
#This file is the largest so far. It contains all the builtin functions.

from stack import Stack
from errors import *
import sys
import System
			
#hold all the builtin functions, store user functions, provide mechanism for retreaving them.
class Functions:
 def __init__(self, stack, parser): #requires a parser and a stack instance.
  #store arguments.
  self.stack=stack
  self.parser=parser
  #Using a hash table, create and store builtin functions.
  self.builtins={
".":self.dot,
",":self.comma,
":":self.colon,
":py":self.pyFunc,
"quit":self.quit,
"ndup":self.ndup,
"count":self.count,
"dup":"2 ndup", #Define builtin with other builtins! It's great!
".count":"count .",
"swap":self.swap,
"rot":self.rot,
"drop":self.drop,
"dswap":self.dswap,
"do":self.do,
"/":self.slash,
"*":self.star,
"+":self.plus,
"-":self.minus,
"?":self.input,
"0=":self.ze,
"<=":self.le,
">=":self.ge,
"<":self.less,
">":self.grater,
"mod":self.modFunc,
"&&":self.andFunc,
"||":self.orFunc,
"not":self.notFunc,
"True":self.tFunc,
"False":self.fFunc,
"if":self.ifFunc,
"var":self.varFunc,
"ld":self.loadVarFunc
}
  #28 builtins so far, need while next.
  #storage for user functions.
  self.userFunc={}
  #symbol table for user variables
  self.symbols={}

  #is there a function called x?
 def isFunc(self, func): #takes string representing the name of the func.
  return self.builtins.has_key(func) or self.userFunc.has_key(func) #check the tables.

 #Ok, we have it, where is it?
 def GetFunc(self, func): #string with the name of the func.
  #which table?
  if self.userFunc.has_key(func): #check user funcs first in case builtin is overwritten.
   return self.userFunc[func] #return user func.
  elif self.builtins.has_key(func): #if it's a builtin,
   return self.builtins[func] #send it back.
  else: #oops, this isn't a func,
   raise Exception("Passed Function class invalid function.") #if we get here, the program itself has a problem, not just a programming error, so we die.
  
  #is there a symbol called x?
 def isSymbol(self, sym): #takes string representing the name of the symbol.
  return self.symbols.has_key(sym) #check the table.

 #Ok, we have it, where is it?
 def GetSymbol(self, sym): #string with the name of the symbol.
  if self.symbols.has_key(sym): #check symbol  table first in case it doesn't exist.
   return self.symbols[sym] #return value stored.
  else: #oops, this isn't a valid symbol,
   raise Exception("Passed invalid symbol.") #if we get here, the program itself has a problem, not just a programming error, so we die.
  
 #. func, (n --		 ) prints the top entry followed by a new line..
 def dot(self):
  print self.stack.pop()

 #, func, (n -- ) same as dot but doesn't add the new line.
 def comma (self):
  sys.stdout.write(str(self.stack.pop()))

 #ndup func ( a b -- a, a, ...*b) create n duplicates, n being the top of the stack, duplicates the next value on the stack.
 def ndup(self):
  (a,n)=self.stack.npop(2) #get a and n.
  #push a n times.
  [self.stack.push(a) for i in range (n)]

 #count ( -- n ) how many items are on the stack.
 def count(self):
  self.stack.push(self.stack.count())

 #: ( -- ) creates a function.
 def colon (self):
  words=self.parser.FindToken(";") #end func deffinition with ;. get everything between : and ;.
  if len(words) == 0: # no ;
   raise CompilingErrorException("While defining a function, you forgot to enter the (;).") #raise programming error.
  else: # good form.
   words.pop() #remove ;
   self.userFunc[words[0]] = " ".join(words[1:])   #add to the users table, the first word is the name.
  #no validation is performed, I don't like it.
 #swap (a,b -- b,a) swap the last two items on the stack.
 def swap (self):
  (b,a) =(self.stack.pop(), self.stack.pop()) #get two items, needs updating.
  #put them back in reverse order.
  self.stack.push(b) 
  self.stack.push(a)

 #drop ( n -- ) forget the top value on the stack.
 def drop(self):
  self.stack.pop()
 #rot func (a, b, c -- b, c, a) moves the third item to the top.
 def rot(self):
  (c,b,a)=(self.stack.pop(),self.stack.pop(),self.stack.pop()) #get the top three values, needs updating.
 #push them back in correct order.
  self.stack.push(b)
  self.stack.push(c)
  self.stack.push(a)

 #dswap func ( a, b, c, d -- c, d, a, b ) swap the top two pairs.
 def dswap(self):
  (a,b,c,d)=self.stack.npop(4) #get 4 variables.
 #put them back correctly.
  self.stack.push(c)
  self.stack.push(d)
  self.stack.push(a)
  self.stack.push(b)
 #0= func ( n -- b) tests if top number = 0.
 def ze(self):
  self.stack.push(self.stack.pop() == 0)

 # <= func (a, b -- a<=b) see if stack value -1 is less or = top stack value.
 def le(self):
  (a,b)=self.stack.npop(2) #get top two values.
  self.stack.push(a<=b)

 #>= (a, b -- a>=b) see if second stack value is grater or equal to the first.
 def ge(self):
  (a,b)=self.stack.npop(2) #get variables.
  self.stack.push(a>=b) 

 #< func (a, b -- a < b) You get the idea.
 def less(self):
  (a,b)=self.stack.npop(2)
  self.stack.push(a<b)
 #> func (a, b -- a > b)
 def grater(self):
  (a,b)=self.stack.npop(2)
  self.stack.push(a>b)
 #+ func (a, b, a+b)
 def plus(self):
  (a,b)=self.stack.npop(2)
  self.stack.push(a+b)
 #- func (a, b -- a-b)
 def minus(self):
  (a,b)=self.stack.npop(2)
  self.stack.push(a-b)

 #/ func (a, b -- a/b) devision
 def slash(self):
  (a,b)=self.stack.npop(2)
  self.stack.push(a/b)
 #* func (a, b -- a*b) multiplication
 def star(self):
  self.stack.push(self.stack.pop() * self.stack.pop())

 #mod func (a, b -- a % b) gets remainder.
 def modFunc(self):
  (a,b)=self.stack.npop(2)
  self.stack.push(a%b)
 #& func (a, b -- a and b) both are True.
 def andFunc(self):
  self.stack.push(self.stack.pop() and self.stack.pop())

 #|| func (a, b -- a or b) one is True.
 def orFunc(self):
  self.stack.push(self.stack.pop() or self.stack.pop())

 #not func (a -- not a) 
 def notFunc(self):
  self.stack.push(not self.stack.pop())

 #True func ( -- True)
 def tFunc(self):
  self.stack.push(True)

 #False func ( -- False)
 def fFunc(self):
  self.stack.push(False)

 #do func (a, b -- ? ) loops a-b times
 def do(self):
  words=self.parser.FindToken("until") #until ends the loop deffinition. Find it and get all the words between.
  if len(words) == 0: #until not found.
   raise CompilingErrorException("You used the word \"do\", which tells me you want to start a loop, but you didn't use the word \"until\", so I don't know where the loop should end.") #programming error.
  else: #It's there.
   (a,b)=self.stack.npop(2) #get looping cycles.
   words=" ".join(words[:-1]) #combine the words between do and until. *** needs validation.
   self.parser.preserve() #preserve the state of the parser.
   for i in range(a,b,-1): #start looping.
    self.parser.ParseLine(words) #do the work.
   self.parser.restore() #restore the state of the parser.
 #quit func ( -- ) die. needs to be renamed.
 def quit (self):
  exit()

 #? func ( -- string) reads a line of data. ** needs converters.
 def input(self):
  self.stack.push(sys.stdin.readline())

 #if func ( a -- ) conditional branch ** broken   
 def ifFunc(self):
  words=self.parser.FindToken("fi") #find end of the statement.
  if len(words) == 0: #not found.
   raise CompilingErrorException("You used the word \"if\", but you didn't tell me where the end is with the word \"fi\".") #programming error.
  else: #well formed if statement.
   words.pop() #remove fi.
   ifblk=words #assume the whole thing doesn't have an else block.
   elseblk = []
   if ifblk.count("else") > 0: #There is an else.
    elsepos=words.index("else")
    #split blocks, remove the word else.
    elseblk=ifblk[elsepos+1:]
    ifblk=ifblk[:elsepos]
   cmp=self.stack.pop() #get the conditional
   if cmp: #test it.
    self.parser.ParseFunc(" ".join(ifblk)) #convert ifblk to string and parse it.
   else:
    self.parser.ParseFunc(" ".join(elseblk)) #convert elseblk to string and parse it.
    
 #:py func (???? -- ???? ) :py any python statement py; ****** remove, for testing only.
 def pyFunc (self):
  #same as do above.
  words = self.parser.FindToken("py;") 
  if words==[]:
   raise CompilingErrorException("While declaring a python string, you didn't end it with \"py;\".")
  else:
   exec(" ".join(words[:-1])) #execute the code.

 #define a variable once before using.
 def varFunc (self):
  #get the name of the variable and create an entry, assigning nutral value.
  self.symbols[self.parser.pop()]=0 #implement error checking. Did we get a word?

#load from stack to the variable.
 def loadVarFunc(self):
  sym=self.parser.pop() #get the name of the variable.
  if len(sym) > 0: #we got a name
   #raise an exception if it's not been registered with var.
   if not self.isSymbol(sym):
    raise CompilingErrorException("You tried to use a variable without declaring it.")
   else: #it's registered.
    self.symbols[sym]=self.stack.pop() #assign the value.
  